<template>
    <div
        ref="jsplumbContainerRef"
        id="jsplumbContainer"
        @drop="dragDrop"
        @dragover.prevent
        class="relative"
        @click="handleContainerClick"
    ></div>
</template>
<script setup>
import { v4 as uuidv4 } from 'uuid';
import { jsPlumb } from 'jsplumb';
import mittBus from '@/utils/mittbus';
import {
    jsplumbSetting,
    jsplumbConnectOptions,
    jsplumbSourceOptions,
    jsplumbTargetOptions,
} from '@/utils/jsplumbConfig';
const jsplumbContainerRef = ref(null);
const jsPlumbRef = ref(null);
const zoom = ref(0.5);
const flowInfo = inject('flowInfo');
const updateFlowNodes = inject('updateFlowNodes');
const updateFlowLines = inject('updateFlowLines');
const currentEl = inject('currentEl');
const divNodeCommanClass =
    'flow-node-drag pl-[10px] py-[10px] pr-[26px] rounded-md border-solid border-[1px] absolute';
const divNodeDefaultClass =
    'border-[#E1E4E8] border-l-[5px] border-l-solid border-l-[#187AFF] cursor-pointer';
const divNodeFocusClass =
    'border-l-[5px] border-l-solid border-l-[#187AFF] border-[#187AFF] bg-[#F0F7FF] cursor-move';
let dragingNode = null;
mittBus.on('drag-start', (node) => {
    dragingNode = node;
});
mittBus.on('update-node-name', (val) => {
    if (currentEl.value) {
        changeCurrentNodeName(val);
    }
});
mittBus.on('update-line-label', (data) => {
    const conn = jsPlumbRef.value.getConnections({
        source: currentEl.value.sourceId,
        target: currentEl.value.targetId,
    })[0];
    conn.setLabel({
        label: data,
    });
});
mittBus.on('update-el-color', (data) => {
    if (currentEl.value) {
        switch (currentEl.value.type) {
            case 'start':
            case 'end':
            case 'node':
                const targetNode = document.getElementById(
                    currentEl.value.id,
                );
                targetNode.style[data.type] = data.value;
                break;
            case 'line':
                console.log(
                    '🚀 ~ mittBus.on ~ data:',
                    data,
                );
                const conn =
                    jsPlumbRef.value.getConnections({
                        source: currentEl.value.sourceId,
                        target: currentEl.value.targetId,
                    })[0];
                if (conn) {
                    const paintStyle = conn.getPaintStyle();
                    paintStyle.stroke =
                        data.value || 'transparent'; // 新增：透明处理
                    conn.setPaintStyle(paintStyle);
                }
                break;
            default:
                break;
        }
    }
});
const changeCurrentNodeName = (nodeName) => {
    const targetNode = document.getElementById(
        currentEl.value.id,
    );
    const boxDiv = targetNode.childNodes[0];
    const nodeNameDiv = boxDiv.childNodes[1];
    nodeNameDiv.innerText = nodeName;
};
const jsPlumbInit = () => {
    jsPlumbRef.value.ready(() => {
        jsPlumbRef.value.importDefaults(jsplumbSetting);
        // 会使整个jsPlumb立即重绘。
        jsPlumbRef.value.setSuspendDrawing(false, true);
        // 单点击了连接线, https://www.cnblogs.com/ysx215/p/7615677.html
        jsPlumbRef.value.bind(
            'click',
            (conn, _originalEvent) => {
                console.log(
                    '🚀 ~ jsPlumbRef.value.ready ~ conn:',
                    conn,
                );
                const sourceId = conn.sourceId;
                const targetId = conn.targetId;
                mittBus.emit('el-click', {
                    type: 'line',
                    sourceId,
                    targetId,
                });
                removeAllElsSelected();
                const paintStyle = conn.getPaintStyle();
                paintStyle.outlineStroke = '#ffeb3b';
                paintStyle.outlineWidth = 3;
                conn.setPaintStyle(paintStyle);
            },
        );
        // 连线
        jsPlumbRef.value.bind('connection', (evt) => {
            console.log(
                '🚀 ~ jsPlumbRef.value.bind connection~ evt:',
                evt,
            );
            const sourceId = evt.sourceId;
            const targetId = evt.targetId;
            updateFlowLines('add', {
                sourceId,
                targetId,
                type: 'line',
                label: '',
                bgColor: '#191919',
            });
        });
        // 删除连线回调
        jsPlumbRef.value.bind(
            'connectionDetached',
            (evt) => {
                console.log(
                    '🚀 ~ jsPlumbRef.value.bind connectionDetached~ evt:',
                    evt,
                );
                const sourceId = evt.source.id;
                const targetId = evt.target.id;
                updateFlowLines('delete', {
                    sourceId,
                    targetId,
                });
            },
        );
        // 改变线的连接节点
        // jsPlumbRef.value.bind('connectionMoved', (evt) => {
        //     console.log(
        //         '🚀 ~ jsPlumbRef.value.bind connectionMoved~ evt:',
        //         evt,
        //     );
        // });
        // 连线右击
        jsPlumbRef.value.bind('contextmenu', (evt) => {
            console.log(
                '🚀 ~ jsPlumbRef.value.bind  contextmenu~ evt:',
                evt,
            );
        });
        // 连线判断
        jsPlumbRef.value.bind('beforeDrop', (evt) => {
            console.log(
                '🚀 ~ jsPlumbRef.value.bind beforeDrop ~ evt:',
                evt,
            );
            const sourceId = evt.sourceId;
            const targetId = evt.targetId;
            if (sourceId === targetId) {
                ElMessage({
                    message: '节点不支持连接自己',
                    type: 'warning',
                });
                return false;
            }
            if (existThisLine(sourceId, targetId)) {
                ElMessage({
                    message: '该关系已存在,不允许重复创建',
                    type: 'warning',
                });
                return false;
            }
            if (existOppositeLine(sourceId, targetId)) {
                ElMessage({
                    message: '不支持两个节点之间连线回环',
                    type: 'warning',
                });
                return false;
            }
            return true;
        });
        // 连线删除前判断
        jsPlumbRef.value.bind('beforeDetach', (evt) => {
            console.log(
                '🚀 ~ jsPlumbRef.value.bind beforeDetach~ evt:',
                evt,
            );
        });
        jsPlumbRef.value.setContainer(
            jsplumbContainerRef.value,
        );
    });
};
const existThisLine = (sourceId, targetId) => {
    for (let i = 0; i < flowInfo.value.lines.length; i++) {
        const line = flowInfo.value.lines[i];
        if (
            line.sourceId === sourceId &&
            line.targetId === targetId
        ) {
            return true;
        }
    }
    return false;
};
const existOppositeLine = (sourceId, targetId) => {
    return existThisLine(targetId, sourceId);
};
onMounted(() => {
    jsPlumbRef.value = jsPlumb.getInstance();
    jsPlumbInit();
});
const emit = defineEmits(['addNode']);
const dragDrop = (event) => {
    if (dragingNode) {
        const type = dragingNode.type;
        const existTypeCount = flowInfo.value.nodes.filter(
            (item) => item.type === type,
        ).length;
        const left = event.offsetX;
        const top = event.offsetY;
        const bgColor = '#FFFFFF';
        const contentColor = '#191919';
        const divNode = document.createElement('div');
        const id = uuidv4();
        divNode.id = id;
        divNode.style.backgroundColor = bgColor;
        divNode.style.color = contentColor;
        divNode.className =
            divNodeCommanClass + ' ' + divNodeDefaultClass;
        divNode.addEventListener('click', (e) => {
            e.stopPropagation();
            if (currentEl.value) {
                if (
                    currentEl.value.id ===
                    e.currentTarget.id
                ) {
                    // 取消选中该节点
                    e.currentTarget.className =
                        divNodeCommanClass +
                        ' ' +
                        divNodeDefaultClass;
                    mittBus.emit('el-click', null);
                } else {
                    removeAllElsSelected();
                    // 选中该节点
                    e.currentTarget.className =
                        divNodeCommanClass +
                        ' ' +
                        divNodeFocusClass;
                    mittBus.emit('el-click', {
                        type: 'node',
                        el: divNode,
                    });
                }
            } else {
                // 选中该节点
                e.currentTarget.className =
                    divNodeCommanClass +
                    ' ' +
                    divNodeFocusClass;
                mittBus.emit('el-click', {
                    type: 'node',
                    el: divNode,
                });
            }
        });
        divNode.style.left = `${left}px`;
        divNode.style.top = `${top}px`;
        const divBox = document.createElement('div');
        divBox.classList.add(
            'flex',
            'flex-row',
            'items-center',
            'gap-[20px]',
        );
        const i = document.createElement('i');
        i.classList.add(
            'iconfont',
            dragingNode.icon,
            'text-[18px]',
            'cursor-crosshair',
            'flow-node-drag',
        );
        const childDiv = document.createElement('div');
        childDiv.classList.add(
            'text-[14px]',
            'leading-[1.2]',
            'font-500',
            'break-all',
            'min-w-[80px]',
            'max-w-[140px]',
        );
        const nodeName = `${flowInfo.value.flowName || '流程'}-${dragingNode.nodeName}${existTypeCount + 1}`;
        childDiv.innerText = nodeName;
        divBox.appendChild(i);
        divBox.appendChild(childDiv);
        divNode.appendChild(divBox);
        jsplumbContainerRef.value.appendChild(divNode);
        updateFlowNodes('add', {
            id,
            left,
            top,
            ...dragingNode,
            nodeName,
            bgColor,
            contentColor,
            remark: '',
        });
        jsPlumbRef.value.makeSource(
            id,
            jsplumbSourceOptions,
        );
        jsPlumbRef.value.makeTarget(
            id,
            jsplumbTargetOptions,
        );
        // 使节点可以拖动
        makeNodeDraggable(id);
        dragingNode = null;
    }
};
const handleContainerClick = () => {
    // 选中的目标元素设为null
    mittBus.emit('el-click', null);
    removeAllElsSelected();
};
// 移除所有元素的选中效果
const removeAllElsSelected = () => {
    // 移除所有节点的选中效果
    flowInfo.value.nodes.forEach((node) => {
        const nodeEl = document.getElementById(node.id);
        if (nodeEl) {
            nodeEl.className =
                divNodeCommanClass +
                ' ' +
                divNodeDefaultClass;
        }
    });
    // 移除所有连线
    flowInfo.value.lines.forEach((line) => {
        const conn = jsPlumbRef.value.getConnections({
            source: line.sourceId,
            target: line.targetId,
        })[0];
        if (conn) {
            const paintStyle = conn.getPaintStyle();
            paintStyle.outlineStroke = 'transparent';
            paintStyle.outlineWidth = 10;
            conn.setPaintStyle(paintStyle);
        }
    });
};
const makeNodeDraggable = (node) => {
    jsPlumbRef.value.draggable(node, {
        containment: 'parent', // 限制节点的拖动范围在父容器内
        // grid: [5, 5], // 使移动只能按照固定的尺寸移动
        stop: (el) => {
            console.log('拖拽结束: ', el);
        },
    });
};
const zoomIn = () => {
    if (zoom.value >= 1) {
        return;
    }
    zoom.value += 0.1;
    jsplumbContainerRef.value.style.transform = `scale(${zoom.value})`;
    jsPlumbRef.value.setZoom(zoom.value);
};
const zoomOut = () => {
    if (zoom.value <= 0.1) {
        return;
    }
    zoom.value -= 0.1;
    jsplumbContainerRef.value.style.transform = `scale(${zoom.value})`;
    jsPlumbRef.value.setZoom(zoom.value);
};
onUnmounted(() => {
    mittBus.off('drag-start');
    mittBus.off('update-node-name');
    mittBus.off('update-line-label');
    mittBus.off('update-el-color');
});
</script>
<style scoped></style>
